#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
using std::ostream;

class CowString
{
public:
    CowString();
    CowString(const char * pstr);
    CowString(const CowString & rhs);
    CowString & operator=(const CowString & rhs);
    ~CowString();
    int size() const
    {
        return strlen(_pstr);
    }

    const char * c_str() const
    {
        return _pstr;
    }

    int use_count(){
        return *(int*)(_pstr - kRefcountLength);
    }
    
    char & operator[](int idx);

    friend
        ostream & operator<<(ostream & os,const CowString & rhs);
private:
    char * malloc(const char * pstr = nullptr)//开辟空间
    {
        if(pstr){
            return new char[strlen(pstr) + 1 + kRefcountLength]() + kRefcountLength;
        }else{
            return new char[1 + kRefcountLength]() + kRefcountLength;
        }
    }

    void release(){ //尝试回收堆空间
        decreaseRefcount();
        if(use_count() == 0){
            delete [] (_pstr - kRefcountLength);
            cout << ">> delete heap" << endl;
        }
        _pstr = nullptr;
    }

    void initRefcount()//初始化引用计数
    {
        *(int*)(_pstr - kRefcountLength) = 1;
    }

    void decreaseRefcount(){
        --*(int*)(_pstr - kRefcountLength);
    }

    void increaseRefcount(){
        ++*(int*)(_pstr - kRefcountLength);
    }


    static const int kRefcountLength = 4;
    char * _pstr;
};

ostream & operator<<(ostream & os,const CowString & rhs){
    if(rhs._pstr){
        os << rhs._pstr;
    }
    return os;
}

CowString::CowString()
    : _pstr(malloc())
{
    cout << "CowString()" << endl;
    initRefcount();
}

CowString::CowString(const char * pstr)
    : _pstr(malloc(pstr))
{
    cout << "CowString(const char *)" << endl;
    strcpy(_pstr,pstr);
    initRefcount();
}


CowString::~CowString()
{
    cout << "~CowString()" << endl;
    release();
}

CowString::CowString(const CowString & rhs)
    : _pstr(rhs._pstr)//浅拷贝
{
    increaseRefcount();
    cout << "CowString(const CowString&)" << endl;
}

CowString & CowString::operator=(const CowString & rhs){
    if(this != &rhs){
        release();//判断是否应该回收堆空间
        _pstr = rhs._pstr; //浅拷贝
        increaseRefcount();
    }
    return *this;
}

char & CowString::operator[](int idx){
    if(idx >= 0 && idx < size()){
        if(use_count() > 1){
            //原本空间的引用计数-1
            decreaseRefcount();
            //进行深拷贝
            char * ptemp = malloc(_pstr);
            strcpy(ptemp,_pstr);
            //改变指向
            _pstr = ptemp;
            //新的空间初始化引用计数
            initRefcount();
        }
        return _pstr[idx];
    }else{
        cout << "out of range" << endl;
        static char nullchar = '\0';
        return nullchar;
    }
}


void test1(){
    CowString str1("hello");
    CowString str2 = str1;
    cout << str1.use_count() << endl;
    cout << str2.use_count() << endl;
    //读写操作无法分开定义逻辑
    /* str2[0] = 'X'; */
    /* cout << str2[0] << endl; */
    cout << "str1:" << str1 << endl;
    cout << "str2:" << str2 << endl;
    cout << str1.use_count() << endl;
    cout << str2.use_count() << endl;
}


void test0(){
    CowString str1;
    CowString str2 = str1;
    cout << "str1:" << str1 << endl;
    cout << "str2:" << str2 << endl;
    cout << str1.use_count() << endl;
    cout << str2.use_count() << endl;

    cout << endl;
    CowString str3("hello");
    cout << "str1:" << str1 << endl;
    cout << "str2:" << str2 << endl;
    cout << "str3:" << str3 << endl;
    cout << str1.use_count() << endl;
    cout << str2.use_count() << endl;
    cout << str3.use_count() << endl;

    cout << endl;
    CowString str4("world");
    str1 = str4;
    cout << "str1:" << str1 << endl;
    cout << "str2:" << str2 << endl;
    cout << "str3:" << str3 << endl;
    cout << "str4:" << str4 << endl;
    cout << str1.use_count() << endl;
    cout << str2.use_count() << endl;
    cout << str3.use_count() << endl;
    cout << str4.use_count() << endl;
}

int main(void){
    test1();
    return 0;
}
